//
//  TextLayoutManager.swift
//  LibUI
//
//  Created by ChangtanSun on 2017/6/1.
//  Copyright © 2017年 ChangtanSun. All rights reserved.
//

import UIKit

public class NSTextAttachmentEx: NSTextAttachment {
    override public func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
        return super.attachmentBounds(for: textContainer, proposedLineFragment: lineFrag, glyphPosition: position, characterIndex: charIndex)
    }
}

public class TextLayoutManager: NSLayoutManager, NSLayoutManagerDelegate {
    public override init() {
        super.init()
        delegate = self
    }

    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override public func drawGlyphs(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
        let range = characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil)

        textStorage?.enumerateAttribute(NSAttributedString.Key.textAttachment, in: range, options: [.longestEffectiveRangeNotRequired], using: { (any, range, _) in
            guard let textAttachment = any as? TextAttachmentContainer else {
                super.drawGlyphs(forGlyphRange: range, at: origin)
                return
            }

            let glyphRange = self.glyphRange(forCharacterRange: range, actualCharacterRange: nil)

            guard let context = UIGraphicsGetCurrentContext(), let container = self.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) else {
                super.drawGlyphs(forGlyphRange: range, at: origin)
                return
            }

            context.saveGState()
            context.translateBy(x: origin.x, y: origin.y)

            let rect = self.boundingRect(forGlyphRange: glyphRange, in: container)

            textAttachment.drawAttachment(context: context, textAttachmentBounds: rect, textContainer: container, characterIndex: glyphRange.location)

            context.restoreGState()
        })
    }

    /// NSLayoutManagerDelegate
    public func layoutManager(_ layoutManager: NSLayoutManager, boundingBoxForControlGlyphAt glyphIndex: Int, for textContainer: NSTextContainer, proposedLineFragment proposedRect: CGRect, glyphPosition: CGPoint, characterIndex charIndex: Int) -> CGRect {
        guard let textStorage = textStorage else {
            return CGRect.zero
        }

        guard let textAttachment = textStorage.attribute(NSAttributedString.Key.textAttachment, at: charIndex, effectiveRange: nil) as? TextAttachmentContainer else {
            return CGRect.zero
        }

        return textAttachment.attachmentBounds(for: textContainer, proposedLineFragment: proposedRect, glyphPosition: glyphPosition, characterIndex: charIndex)
    }

    public func layoutManager(_ layoutManager: NSLayoutManager, shouldUse action: NSLayoutManager.ControlCharacterAction, forControlCharacterAt charIndex: Int) -> NSLayoutManager.ControlCharacterAction {
        if action == .zeroAdvancement {
            return .whitespace
        } else {
            return action
        }
    }

    public func layoutManager(_ layoutManager: NSLayoutManager, shouldBreakLineByWordBeforeCharacterAt charIndex: Int) -> Bool {
        return true
    }

    public func layoutManager(_ layoutManager: NSLayoutManager, shouldSetLineFragmentRect lineFragmentRect: UnsafeMutablePointer<CGRect>, lineFragmentUsedRect: UnsafeMutablePointer<CGRect>, baselineOffset: UnsafeMutablePointer<CGFloat>, in textContainer: NSTextContainer, forGlyphRange glyphRange: NSRange) -> Bool {

        if let textAttachment = layoutManager.textStorage?.attribute(NSAttributedString.Key.textAttachment, at: glyphRange.location, effectiveRange: nil) as? TextAttachmentContainer {

            return textAttachment.layoutManager(layoutManager, shouldSetLineFragmentRect: lineFragmentRect, lineFragmentUsedRect: lineFragmentUsedRect, baselineOffset: baselineOffset, in: textContainer, forGlyphRange: glyphRange)
        }

        return true
    }

    public func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
        if let textAttachment = layoutManager.textStorage?.attribute(NSAttributedString.Key.textAttachment, at: glyphIndex, effectiveRange: nil) as? TextAttachmentContainer {

            return textAttachment.layoutManager(layoutManager, lineSpacingAfterGlyphAt: glyphIndex, withProposedLineFragmentRect: rect)
        } else if let paragraphStyle = layoutManager.textStorage?.attribute(.paragraphStyle, at: glyphIndex, effectiveRange: nil) as? NSParagraphStyle {

            return paragraphStyle.lineSpacing
        }

        return 0
    }
}
